package com.event.gateway.filter;

import com.event.gateway.config.ImagePathConfig;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.core.io.buffer.DefaultDataBufferFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.core.scheduler.Schedulers;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 静态资源过滤器工厂
 * 用于处理图片等静态资源的访问
 */
@Component
@Slf4j
public class StaticResourceFilterFactory extends AbstractGatewayFilterFactory<StaticResourceFilterFactory.Config> {

    @Value("${image.base-dir:eventsys}")
    private String baseDir;

    @Value("${image.upload-folder:uploads/images}")
    private String uploadFolder;
    
    // 使用缓存存储文件类型映射，提高性能
    private static final ConcurrentHashMap<String, String> CONTENT_TYPE_CACHE = new ConcurrentHashMap<>();

    public StaticResourceFilterFactory() {
        super(Config.class);
    }

    @Override
    public List<String> shortcutFieldOrder() {
        return Collections.emptyList();
    }

    @Override
    public GatewayFilter apply(Config config) {
        return (exchange, chain) -> {
            String path = exchange.getRequest().getURI().getPath();
            log.debug("处理静态资源请求: {}", path);

            // 提取图片路径，去掉前缀/images/
            String imagePath = path.startsWith("/images/") 
                ? path.substring("/images/".length()) 
                : path;

            // 获取eventsys根目录（使用ImagePathConfig中的缓存）
            String rootDir = ImagePathConfig.getRootDir();
            if (rootDir == null) {
                log.error("无法找到eventsys根目录");
                exchange.getResponse().setStatusCode(HttpStatus.NOT_FOUND);
                return exchange.getResponse().setComplete();
            }

            // 构建完整的文件路径
            String filePathStr = Paths.get(rootDir, uploadFolder, imagePath).toString();
            Path filePath = Paths.get(filePathStr);
            log.debug("尝试访问文件: {}", filePathStr);

            // 检查文件是否存在
            if (!Files.exists(filePath)) {
                log.error("文件不存在: {}", filePathStr);
                exchange.getResponse().setStatusCode(HttpStatus.NOT_FOUND);
                return exchange.getResponse().setComplete();
            }

            // 读取文件并返回（使用响应式方式）
            ServerHttpResponse response = exchange.getResponse();
            response.setStatusCode(HttpStatus.OK);
            
            // 根据文件扩展名设置Content-Type（使用缓存）
            String contentType = getContentType(filePath);
            response.getHeaders().add(HttpHeaders.CONTENT_TYPE, contentType);
            
            // 设置缓存控制
            response.getHeaders().add(HttpHeaders.CACHE_CONTROL, "max-age=3600");
            
            // 使用响应式方式读取文件并写入响应
            return readFileReactively(filePath, response);
        };
    }
    
    /**
     * 使用响应式方式读取文件并写入响应
     */
    private Mono<Void> readFileReactively(Path filePath, ServerHttpResponse response) {
        return Mono.fromCallable(() -> {
            // 获取文件大小
            try {
                long fileSize = Files.size(filePath);
                response.getHeaders().add(HttpHeaders.CONTENT_LENGTH, String.valueOf(fileSize));
                return Files.newInputStream(filePath);
            } catch (IOException e) {
                throw new RuntimeException("无法读取文件: " + filePath, e);
            }
        })
        .subscribeOn(Schedulers.boundedElastic())
        .flatMap(inputStream -> {
            Flux<DataBuffer> dataBufferFlux = DataBufferUtils.readInputStream(
                () -> inputStream, 
                new DefaultDataBufferFactory(), 
                4096 // 缓冲区大小
            );
            return response.writeWith(dataBufferFlux);
        })
        .onErrorResume(e -> {
            log.error("读取文件失败: " + filePath, e);
            response.setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR);
            return response.setComplete();
        });
    }

    /**
     * 获取文件内容类型（带缓存）
     */
    private String getContentType(Path filePath) {
        String fileName = filePath.getFileName().toString().toLowerCase();
        
        // 尝试从缓存获取
        return CONTENT_TYPE_CACHE.computeIfAbsent(fileName, k -> {
            try {
                String contentType = Files.probeContentType(filePath);
                if (contentType != null) {
                    return contentType;
                }
            } catch (IOException e) {
                log.warn("无法确定文件类型: {}", filePath);
            }
            
            // 根据扩展名判断
            if (fileName.endsWith(".jpg") || fileName.endsWith(".jpeg")) {
                return MediaType.IMAGE_JPEG_VALUE;
            } else if (fileName.endsWith(".png")) {
                return MediaType.IMAGE_PNG_VALUE;
            } else if (fileName.endsWith(".gif")) {
                return MediaType.IMAGE_GIF_VALUE;
            } else if (fileName.endsWith(".svg")) {
                return "image/svg+xml";
            } else if (fileName.endsWith(".mp4")) {
                return "video/mp4";
            }
            
            // 默认为二进制流
            return MediaType.APPLICATION_OCTET_STREAM_VALUE;
        });
    }

    /**
     * 配置类
     */
    public static class Config {
        // 配置参数可以在这里定义
    }
} 